﻿#region File Description
//-----------------------------------------------------------------------------
// Game.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using DS.SludgeEngine.Base;
#endregion

namespace DS.SludgeEngine.Particles
{
    public class Emitter
    {
        private int particlesLeft;
        private int particlesPerFrame;

        public int ParticlesLeft
        {
            get { return particlesLeft; }
            set { particlesLeft = value; }
        }
        public int ParticlesPerFrame
        {
            get { return particlesPerFrame; }
            set { particlesPerFrame = value; }
        }
        private Vector3 location;

        public Vector3 Location
        {
            get { return location; }
            set { location = value; }
        }
    }

    /// <summary>
    /// Sample showing how to implement a particle system entirely
    /// on the GPU, using the vertex shader to animate point sprites.
    /// </summary>
    public class ParticleController : DrawableGameComponent, IParticleController
    {
        #region Fields



        SpriteBatch spriteBatch;


        // This sample uses five different particle systems.
        ParticleSystem explosionParticles;
        ParticleSystem explosionSmokeParticles;
        ParticleSystem projectileTrailParticles;
        ParticleSystem smokePlumeParticles;
        ParticleSystem fireParticles;


        // The sample can switch between three different visual effects.
        enum ParticleState
        {
            Explosions,
            SmokePlume,
            RingOfFire,
        };

        ParticleState currentState = ParticleState.Explosions;


        // The explosions effect works by firing projectiles up into the
        // air, so we need to keep track of all the active projectiles.
        List<Projectile> projectiles = new List<Projectile>();

        TimeSpan timeToNextProjectile = TimeSpan.Zero;


        // Random number generator for the fire effect.
        Random random = new Random();



        #endregion
        private Dictionary<string, ParticleEmitter> trailEmitters = new Dictionary<string, ParticleEmitter>();
        private List<Emitter> smokeEmitters = new List<Emitter>();
        private List<Emitter> fireEmitters = new List<Emitter>();
        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public ParticleController(Game game)
            : base(game)
        {
            spriteBatch = new SpriteBatch(game.GraphicsDevice);

            // Construct our particle system components.
            explosionParticles = new ExplosionParticleSystem(game, game.Content);
            explosionSmokeParticles = new ExplosionSmokeParticleSystem(game, game.Content);
            projectileTrailParticles = new ProjectileTrailParticleSystem(game, game.Content);
            smokePlumeParticles = new SmokePlumeParticleSystem(game, game.Content);
            fireParticles = new FireParticleSystem(game, game.Content);

            // Set the draw order so the explosions and fire
            // will appear over the top of the smoke.
            smokePlumeParticles.DrawOrder = 100;
            explosionSmokeParticles.DrawOrder = 200;
            projectileTrailParticles.DrawOrder = 300;
            explosionParticles.DrawOrder = 400;
            fireParticles.DrawOrder = 500;

            // Register the particle system components.
            game.Components.Add(explosionParticles);
            game.Components.Add(explosionSmokeParticles);
            game.Components.Add(projectileTrailParticles);
            game.Components.Add(smokePlumeParticles);
            game.Components.Add(fireParticles);
        }


        /// <summary>
        /// Load your graphics content.
        /// </summary>
        protected void LoadContent()
        {

        }


        #endregion

        #region Update and Draw

        public void CreateTrailEmitter(string key, float particlesPerSecond, Vector3 position)
        {
            ParticleEmitter emitter = new ParticleEmitter(projectileTrailParticles, particlesPerSecond, position);
            trailEmitters.Add(key, emitter);
        }

        public void UpdateTrailEmitter(string key, GameTime gameTime, Vector3 position)
        {
            trailEmitters[key].Update(gameTime, position);
        }

        public void DeleteTrailEmitter(string key)
        {
            trailEmitters.Remove(key);
        }

        /// <summary>
        /// Allows the game to run logic.
        /// </summary>
        public override void Update(GameTime gameTime)
        {
            List<Emitter> extinguished = new List<Emitter>();

            foreach (Emitter emitter in smokeEmitters)
            {
                emitter.ParticlesLeft--;
                if (emitter.ParticlesLeft <= 0)
                {
                    extinguished.Add(emitter);
                }
                smokePlumeParticles.AddParticle(emitter.Location, Vector3.Zero);
            }

            foreach (Emitter emitter in extinguished)
            {
                smokeEmitters.Remove(emitter);
            }
            extinguished.Clear();

            foreach (Emitter emitter in fireEmitters)
            {
                emitter.ParticlesLeft--;
                if (emitter.ParticlesLeft <= 0)
                {
                    extinguished.Add(emitter);
                }
                

                // Create a number of fire particles, randomly positioned around a circle.
                for (int i = 0; i < emitter.ParticlesPerFrame; i++)
                {
                    fireParticles.AddParticle(emitter.Location, Vector3.Zero);
                }

                // Create one smoke particle per frmae, too.
                smokePlumeParticles.AddParticle(emitter.Location, Vector3.Zero);
            }

            foreach (Emitter emitter in extinguished)
            {
                fireEmitters.Remove(emitter);
            }

            //switch (currentState)
            //{
            //    case ParticleState.Explosions:
            //        UpdateExplosions(gameTime);
            //        break;

            //    case ParticleState.SmokePlume:
            //        UpdateSmokePlume();
            //        break;

            //    case ParticleState.RingOfFire:
            //        UpdateFire();
            //        break;
            //}

            //UpdateProjectiles(gameTime);
        }


        /// <summary>
        /// Helper for updating the explosions effect.
        /// </summary>
        void UpdateExplosions(GameTime gameTime)
        {
            timeToNextProjectile -= gameTime.ElapsedGameTime;

            if (timeToNextProjectile <= TimeSpan.Zero)
            {
                // Create a new projectile once per second. The real work of moving
                // and creating particles is handled inside the Projectile class.
                projectiles.Add(new Projectile(explosionParticles,
                                               explosionSmokeParticles,
                                               projectileTrailParticles));

                timeToNextProjectile += TimeSpan.FromSeconds(1);
            }
        }


        /// <summary>
        /// Helper for updating the list of active projectiles.
        /// </summary>
        void UpdateProjectiles(GameTime gameTime)
        {
            int i = 0;

            while (i < projectiles.Count)
            {
                if (!projectiles[i].Update(gameTime))
                {
                    // Remove projectiles at the end of their life.
                    projectiles.RemoveAt(i);
                }
                else
                {
                    // Advance to the next projectile.
                    i++;
                }
            }
        }


        /// <summary>
        /// Helper for updating the smoke plume effect.
        /// </summary>
        void UpdateSmokePlume()
        {
            // This is trivial: we just create one new smoke particle per frame.
            smokePlumeParticles.AddParticle(Vector3.Zero, Vector3.Zero);
        }


        /// <summary>
        /// Helper for updating the fire effect.
        /// </summary>
        void UpdateFire()
        {
            const int fireParticlesPerFrame = 20;

            // Create a number of fire particles, randomly positioned around a circle.
            for (int i = 0; i < fireParticlesPerFrame; i++)
            {
                fireParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
            }

            // Create one smoke particle per frmae, too.
            smokePlumeParticles.AddParticle(RandomPointOnCircle(), Vector3.Zero);
        }


        /// <summary>
        /// Helper used by the UpdateFire method. Chooses a random location
        /// around a circle, at which a fire particle will be created.
        /// </summary>
        Vector3 RandomPointOnCircle()
        {
            const float radius = 30;
            const float height = 40;

            double angle = random.NextDouble() * Math.PI * 2;

            float x = (float)Math.Cos(angle);
            float y = (float)Math.Sin(angle);

            return new Vector3(x * radius, y * radius + height, 0);
        }

        const int numExplosionParticles = 30;
        const int numExplosionSmokeParticles = 50;

        public void CreateExplosion(Vector3 location)
        {
            for (int i = 0; i < numExplosionParticles; i++)
                explosionParticles.AddParticle(location, Vector3.Zero);

            for (int i = 0; i < numExplosionSmokeParticles; i++)
                explosionSmokeParticles.AddParticle(location, Vector3.Zero);
        }

        public void CreateSmoke(Vector3 location, int length)
        {
            Emitter emitter = new Emitter();
            emitter.Location = location;
            emitter.ParticlesLeft = length;
            smokeEmitters.Add(emitter);
        }


        public void CreateFire(Vector3 location, int particlesPerFrame, int length)
        {
            Emitter emitter = new Emitter();
            emitter.Location = location;
            emitter.ParticlesLeft = length;
            emitter.ParticlesPerFrame = particlesPerFrame;
            fireEmitters.Add(emitter);
        }


        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            ICameraComponent camera = (ICameraComponent)base.Game.Services.GetService(typeof(ICameraComponent));

            Matrix view = camera.ViewMatrix;
            Matrix projection = camera.ProjectionMatrix;


            // Pass camera matrices through to the particle system components.
            explosionParticles.SetCamera(view, projection);
            explosionSmokeParticles.SetCamera(view, projection);
            projectileTrailParticles.SetCamera(view, projection);
            smokePlumeParticles.SetCamera(view, projection);
            fireParticles.SetCamera(view, projection);


        }
        #endregion





    }
}

